/* ***** BEGIN LICENSE BLOCK *****
 *   Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Youtube Tooltip.
 *
 * The Initial Developer of the Original Code is
 * Brandon Peters.
 * Portions created by the Initial Developer are Copyright (C) 2008
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 * 
 * ***** END LICENSE BLOCK ***** */

var youtubeTooltip = {
	prefs: {},
	prefService: null,
	originalTooltip: null,
	Ci: Components.interfaces,
	Cc: Components.classes,

	register: function()
	{
		var prefService = this.Cc["@mozilla.org/preferences-service;1"]
			.getService(this.Ci.nsIPrefBranch);

		this._prefBranch = prefService.getBranch('extensions.{D48A12C2-E0F4-4640-8ADE-2DE4959E0F3D}.');
		this._prefBranch.QueryInterface(this.Ci.nsIPrefBranch2);
		this._prefBranch.addObserver('', this, false);
	},
	unregister: function()
	{
		if(!this._prefBranch)
		{
			return;
		}

		this._prefBranch.removeObserver('', this);
	},
	observe: function(aSubject, aTopic, aData)
	{
		if(aTopic != 'nsPref:changed')
		{
			return;
		}

		switch(this._prefBranch.getPrefType(aData))
		{
			case this.Ci.nsIPrefBranch.PREF_STRING:
				this.prefs[aData] = this._prefBranch.getCharPref(aData);
				break;
			case this.Ci.nsIPrefBranch.PREF_INT:
				this.prefs[aData] = this._prefBranch.getIntPref(aData);
				break;
			case this.Ci.nsIPrefBranch.PREF_BOOL:
				this.prefs[aData] = this._prefBranch.getBoolPref(aData);
				break;
		}
	},
	initialize: function()
	{
		this.prefService = this.Cc["@mozilla.org/preferences-service;1"]
			.getService(this.Ci.nsIPrefService)
			.getBranch('extensions.{D48A12C2-E0F4-4640-8ADE-2DE4959E0F3D}.');

		this.prefService.QueryInterface(this.Ci.nsIPrefBranch2);

		var preferences = [
			'showauthor',
			'showrating',
			'showviews',
			'showduration',
			'showdate',
			'showdescription',
			'showcategory',
			'showtags',
			'showcomments',
			'showresponses'
		];

		for(var idx in preferences)
		{
			var preference = preferences[idx];
	
			try
			{
				this.prefs[preference] = this.prefService.getBoolPref(preference);
			}
			catch(ex)
			{
				this.prefs[preference] = false;
			}
		}

		this.strBundle = document.getElementById('youtubetooltip-strings');

		this.register();

		this.originalTooltip = window.FillInHTMLTooltip;
		window.FillInHTMLTooltip = this.tooltip;
	},
	shutdown: function()
	{
		this.unregister();
	},
	tooltip: function(tipElement)
	{
		var retVal = false;
	
		if(tipElement.namespaceURI == 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul')
		{
			return retVal;
		}
	
		var newElement = tipElement;
		while(newElement && (newElement.nodeType != Node.ELEMENT_NODE || newElement.tagName != 'A')) {
			newElement = newElement.parentNode;
		}
		
		if(!newElement) {
			return youtubeTooltip.originalTooltip(tipElement);
		}

		var linkHREF = newElement.getAttribute('href');
		if(!linkHREF)
		{
			return youtubeTooltip.originalTooltip(tipElement);
		}
		
		linkHREF = unescape(linkHREF);
		
		var defView = newElement.ownerDocument.defaultView;
		if(!defView)
		{
			return retVal;
		}
		var direction = defView.getComputedStyle(newElement, '').getPropertyValue('direction');
	
		var tipNode = document.getElementById('aHTMLTooltip');
		tipNode.style.direction = direction;
	
		var patterns = [
			[/http(s?):\/\/([A-Za-z0-9]*)(\.?)youtube\.com\/watch\?v=([A-Za-z0-9-_]+)/, 4],
			[/\/watch\?v=([A-Za-z0-9-_]+)/, 1],
		
		];
	
		for(var j = 0; j < patterns.length; j++)
		{
			var re = patterns[j][0];
			var idx = patterns[j][1];
			if(re.test(linkHREF))
			{
				var hrefArray = re.exec(linkHREF);
	
				var yID = hrefArray[idx];
				var yData = youtubeTooltip.data(yID);

				var tipLabel = yData.title;
				if(youtubeTooltip.prefs['showauthor'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('author') + ': ' + yData.author;
				}
			
				if(youtubeTooltip.prefs['showduration'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('duration') + ': ' + yData.duration;
				}
		
				if(youtubeTooltip.prefs['showrating'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('rating') + ': ' + yData.rating;
				}
		
				if(youtubeTooltip.prefs['showviews'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('views') + ': ' + yData.views;
				}
		
				if(youtubeTooltip.prefs['showdate'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('date') + ': ' + yData.date;
				}
		
				if(youtubeTooltip.prefs['showdescription'])
				{
					if(yData.description.length > 500)
					{
						yData.description = yData.description.slice(0, 499) + '...';
					}
					
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('description') + ': ' + yData.description.slice(0, 499);
				}
		
				if(youtubeTooltip.prefs['showcategory'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('category') + ': ' + yData.category;
				}
		
				if(youtubeTooltip.prefs['showtags'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('tags') + ': ' + yData.tags;
				}
		
				if(youtubeTooltip.prefs['showcomments'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('comments') + ': ' + yData.comments;
				}
		
				if(youtubeTooltip.prefs['showresponses'])
				{
					tipLabel += "\n" + youtubeTooltip.strBundle.getString('responses') + ': ' + yData.responses;
				}
		
				if(tipLabel != null && yData.title != null)
				{
					tipNode.setAttribute('label', tipLabel);
					retVal = true;
				}
			}
		}
	
		if(!retVal)
		{
			return youtubeTooltip.originalTooltip(tipElement);
		}
	
		return retVal;
	},
	data: function(videoID)
	{
		var novideo = youtubeTooltip.strBundle.getString('novideo');
		var unknown = youtubeTooltip.strBundle.getString('unknown');

		var yData = {
			title: novideo,
			author: unknown,
			rating: unknown,
			views: unknown,
			duration: unknown,
			'date': unknown,
			description: unknown,
			category: unknown,
			tags: unknown,
			comments: unknown,
			responses: unknown
		};
	
		const xmlNS = 'http://www.w3.org/2005/Atom';
		const ytNS = 'http://gdata.youtube.com/schemas/2007'; 
		const gdNS = 'http://schemas.google.com/g/2005';
		const mediaNS = 'http://search.yahoo.com/mrss/';
		const osNS = 'http://a9.com/-/spec/opensearchrss/1.0/';
	
		var gdataURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID;
		var commentURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID + '/comments';
		var responseURL = 'http://gdata.youtube.com/feeds/api/videos/' + videoID + '/responses';
	
		var req = new XMLHttpRequest();
		req.open('GET', gdataURL, false);
		req.send(null);
	
		if(req.status != 200)
		{
			return yData;
		}
	
		var videoDom = req.responseXML;	
		if(videoDom.documentElement.nodeName == 'parseerror')
		{
			return yData;
		}
	
		/* Title */
		var titles = videoDom.getElementsByTagNameNS(xmlNS, 'title');
		if(titles && titles.length > 0)
		{
			var titleElement = titles[0];
			if(titleElement && titleElement.textContent)
			{
				yData.title = titleElement.textContent;
			}
		}
	
		/* Author */
		var authors = videoDom.getElementsByTagNameNS(xmlNS, 'author');
		if(authors && authors.length > 0)
		{
			var authorElement = authors[0];
			if(authorElement && authorElement.firstChild)
			{
				var nameElement = authorElement.firstChild;
				if(nameElement && nameElement.textContent)
				{
					yData.author = nameElement.textContent;
				}
			}
		}
	
		/* Rating */
		var ratings = videoDom.getElementsByTagNameNS(gdNS, 'rating');
		if(ratings && ratings.length > 0)
		{
			var ratingElement = ratings[0];
			if(ratingElement && ratingElement.getAttribute('average'))
			{
				yData.rating = Math.round(ratingElement.getAttribute('average') * 100) / 100;
			}
		}
	
		/* Number of Views */
		var views = videoDom.getElementsByTagNameNS(ytNS, 'statistics');
		if(views && views.length > 0)
		{
			var viewElement = views[0];
			if(viewElement && viewElement.getAttribute('viewCount'))
			{
				yData.views = this.addCommas(viewElement.getAttribute('viewCount'));
			}
		}
	
		/* Duration */
		var durations = videoDom.getElementsByTagNameNS(ytNS, 'duration');
		if(durations && durations.length > 0)
		{
			var durationElement = durations[0];
			if(durationElement && durationElement.getAttribute('seconds'))
			{
				yData.duration = '';
	
				var seconds = durationElement.getAttribute('seconds');
				var minutes = 0;
				var hours = 0;
	
				if(seconds > 60)
				{
					minutes = (seconds - (seconds % 60)) / 60;
					seconds = seconds % 60;
				}
	
				if(minutes > 60)
				{
					hours = (minutes - (minutes % 60)) / 60;
					minutes = minutes % 60;
				}

				//TODO: We need to take into account locale and how time is formatted
				if(hours > 0)
				{
					yData.duration += hours + 'h';
				}
	
				if(minutes > 0)
				{
					yData.duration += minutes + 'm';
				}
	
				if(seconds > 0)
				{
					yData.duration += seconds + 's';
				}
			}
		}
	
		/* Date Published */
		var dates = videoDom.getElementsByTagNameNS(xmlNS, 'published');
		if(dates && dates.length > 0)
		{
			var dateElement = dates[0];
			if(dateElement && dateElement.textContent)
			{
				var re = '([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})';
				var dateStr = dateElement.textContent;
				var matches = dateStr.match(new RegExp(re));
				if(matches)
				{
					//TODO: We need to take into account the locale and how dates are formatted
					//Date.toLocaleString maybe?
					yData.date = matches[2] + '/' + matches[3] + '/' + matches[1] + ' ' + matches[4] + ':' + matches[5] + ':' + matches[6];
				}
			}
		}
	
		/* Description */
		var descriptions = videoDom.getElementsByTagNameNS(xmlNS, 'content');
		if(descriptions && descriptions.length > 0)
		{
			var descriptionElement = descriptions[0];
			if(descriptionElement && descriptionElement.textContent)
			{
				yData.description = descriptionElement.textContent;
			}
		}
		
		/* Category */
		var categories = videoDom.getElementsByTagNameNS(mediaNS, 'category');
		if(categories && categories.length > 0)
		{
			var categoryElement = categories[0];
			if(categoryElement && categoryElement.textContent)
			{
				yData.category = categoryElement.textContent;
			}
		}
	
		/* Tags */
		var tags = videoDom.getElementsByTagNameNS(mediaNS, 'keywords');
		if(tags && tags.length > 0)
		{
			var tagElement = tags[0];
			if(tagElement && tagElement.textContent)
			{
				yData.tags = tagElement.textContent;
			}
		}
	
		/* Comments Entry */
		var req = new XMLHttpRequest();
		req.open('GET', commentURL, false);
		req.send(null);
	
		if(req.status != 200)
		{
			return yData;
		}
	
		var commentDom = req.responseXML;	
		if(commentDom.documentElement.nodeName == 'parseerror')
		{
			return yData;
		}
	
		/* Number of Comments */
		var comments = commentDom.getElementsByTagNameNS(osNS, 'totalResults');
		if(comments && comments.length > 0)
		{
			var commentElement = comments[0];
			if(commentElement && commentElement.textContent)
			{
				yData.comments = this.addCommas(commentElement.textContent);
			}
		}
	
		/* Responses Entry */
		var req = new XMLHttpRequest();
		req.open('GET', responseURL, false);
		req.send(null);
	
		if(req.status != 200)
		{
			return yData;
		}
	
		var responseDom = req.responseXML;	
		if(responseDom.documentElement.nodeName == 'parseerror')
		{
			return yData;
		}
	
		/* Number of Responses */
		var responses = responseDom.getElementsByTagNameNS(osNS, 'totalResults');
		if(responses && responses.length > 0)
		{
			var responseElement = responses[0];
			if(responseElement && responseElement.textContent)
			{
				yData.responses = this.addCommas(responseElement.textContent);
			}
		}
	
		return yData;
	},
	addCommas: function(num)
	{
		//TODO: We need to take into account locale and how numbers are formatted
 		var re = /(\d+)(\d{3})/;
		while(re.test(num))
		{
			num = num.replace(re, '$1' + ',' + '$2');
		}
		return num;
	}
};

window.addEventListener('load', function(e) { youtubeTooltip.initialize(e); }, false);
window.addEventListener('unload', function(e) { youtubeTooltip.shutdown(e); }, false);
